Option Explicit

'################################################################
' Descriptor
' 基础类型数据的数组引用
'################################################################

Implements IObjArrayRef

'################################################################
' Field
'################################################################
' 原始数组引用
Private delegatedArrRef As IObjArrayRef

' ------------------------------
' 在当前切片内部通过索引的计算来管理引用的数据
' ------------------------------
' 切片的起始索引
Private sliceStartIndex As Long

' 切片的步长
Private sliceStep As Long

' 当前切片的最大长度
' 运行时不变
Private sliceMaxIndex As Long

'################################################################
' Constructor
'################################################################
Private Sub Class_Initialize()
    sliceStartIndex = -1
    sliceStep = -1
    sliceMaxIndex = -1
End Sub

Private Sub Class_Terminate()
End Sub

'################################################################
' Init
'################################################################
' @param origin 原始 IObjArrayRef 对象
' @optional-param start 起始索引
' @optional-param stop 终止结束(最终不会包含 stop 的索引)
' @optional-param step 步长，默认 = 1，但是不能是 0
Public Sub Init(ByRef origin As IObjArrayRef, Optional ByRef startIdx As Variant, Optional ByRef stopIdx As Variant, Optional ByRef step As Variant=1)
    ' 1. 计算切片信息
    Dim sliceInfo As Variant
    sliceInfo = UtilsArray.computeArraySliceInfo(origin.Count, startIdx, stopIdx, step)

    ' 2. 保存被代理的数据对象
    Set delegatedArrRef = origin

    ' 3. 如果切片的计算结果中没有数据，则立刻终止
    If sliceInfo(3) <= 0 Then
        Exit Sub
    End If

    ' 4. 保存各个属性
    sliceStartIndex = sliceInfo(0)
    sliceStep = sliceInfo(2)
    sliceMaxIndex = sliceInfo(3) - 1
End Sub

'################################################################
' Property
'################################################################
' 获取当前数组引用中已使用的长度
Public Property Get IObjArrayRef_Count() As Long
    IObjArrayRef_Count = sliceMaxIndex + 1
End Property

' 获取当前数组引用的最大索引
Public Property Get IObjArrayRef_MaxIndex() As Long
    IObjArrayRef_MaxIndex = sliceMaxIndex
End Property

'################################################################
' Function
'################################################################
' 添加 count 个空数据到数组中
' 如果容量不足，将会自动扩容
' 空数据将会同一被设置成 Empty
'
' @param count 需要添加的空数据数量
Public Sub IObjArrayRef_AddNull(ByRef count As Long)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_AddNull: method is not support"
End Sub

' 获取指定索引下的数据
' @param index 数组索引，如果越界将会抛出异常
Public Function IObjArrayRef_Item(ByRef index As Long) As Variant
    ' 1. 检查参数
    If index < 0 Then
        Err.Raise 10010, Description:="ObjArrayRefSlice.IObjArrayRef_Item: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Function
    End If
    If index > sliceMaxIndex Then
        Err.Raise 10010, Description:="ObjArrayRefSlice.IObjArrayRef_Item: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Function
    End If

    ' 2. 返回数据
    '    计算真实索引 = 切片起始索引 + 目标索引 * 步长
    Set IObjArrayRef_Item = delegatedArrRef.unsafeItem(sliceStartIndex + index * sliceStep)
End Function

' 获取指定索引下的数据
' 获取数据时，将不会检查索引的范围，如果索引越界会产生异常
' 在可以保证索引一定安全的情况下，可以通过该方法来获取数据，通过省略索引范围检查来提升效率
' @param index 数组索引，如果越界将会抛出异常
Public Function IObjArrayRef_unsafeItem(ByRef index As Long) As Variant
    Set IObjArrayRef_Item = delegatedArrRef.unsafeItem(sliceStartIndex + index * sliceStep)
End Function

' 设置指定索引下的数据
' @param index 数组索引，如果越界将会抛出异常
' @param val 需要保存到数组中的数据
Public Sub IObjArrayRef_setVal(ByRef index As Long, ByRef val As Variant)
    ' 1. 检查参数
    If index < 0 Then
        Err.Raise 10010, Description:="ObjArrayRefSlice.IObjArrayRef_setVal: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Sub
    End If
    If index > sliceMaxIndex Then
        Err.Raise 10010, Description:="ObjArrayRefSlice.IObjArrayRef_setVal: parameter[index]=" & index & " out of bounds. min index=0, max index[" & sliceMaxIndex & "]"
        Exit Sub
    End If

    If Not IsObject(val) Then
        Err.Raise 10004, Description:="ObjArrayRefSlice.IObjArrayRef_setVal: parameter val must be not Object or Nothing"
        Exit Sub
    End If

    ' 2. 设置数据
    delegatedArrRef.setVal sliceStartIndex + index * sliceStep, val
End Sub

' 向数组末尾添加数据，如果数组容量不足将会自动扩容
' @param val 需要保存到数组中的数据
Public Sub IObjArrayRef_Add(ByRef val As Variant)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_Add: method is not support"
End Sub

' 从数组的开头添加数据，如果数组容量不足将会自动扩容
' @param val 需要保存到数组中的数据
Public Sub IObjArrayRef_AddFirst(ByRef val As Variant)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_AddFirst: method is not support"
End Sub

' 将数据 `val` 插入索引 `index` 的位置
' 支持的索引范围为 `0--IObjArrayRef.Count()`
'
' @param index 数据需要插入的索引
' @param val 需要保存到数组中的**对象类型数据**
Public Sub IObjArrayRef_Insert(ByRef index As Long, ByRef val As Variant)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_Insert: method is not support"
End Sub

' 清空数组内容
'
' @optional-param initSize 当前参数在切片下无效
Public Sub IObjArrayRef_clear(Optional ByRef initSize As Long = -1)
    Dim i As Long
    For i = 0 To sliceMaxIndex
        delegatedArrRef.setVal sliceStartIndex + i * sliceStep, Nothing
    Next
End Sub

' 删除指定索引下的数据
'
' 如果索引超出了数组的边界，不会抛出异常，默认不会执行任何操作
' 该操作会使数组的可用长度 - 1
'
' @param index 数组索引
Public Sub IObjArrayRef_remove(ByRef index As Long)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_remove: method is not support"
End Sub

' 尝试从当前数组中搜索与参数 val 相同的数据，并删除
'
' 如果 needAll=False, 则只会删除第一个与 val 相同的数据
' 如果 needAll=True, 则会遍历整个数组并删除与 val 相同的所有数据
' 该操作会使数组的可用长度变小
'
' @param val, 需要删除的数据
' @optinal-param needAll 是否需要删除所有相同的数据
'                默认=False，只删除第一个和 val 相同的数据
Public Sub IObjArrayRef_removeVal(ByRef val As Variant, Optional ByRef needAll As Boolean = False)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_removeVal: method is not support"
End Sub

' 删除最后一个元素
Public Sub IObjArrayRef_removeLast()
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_removeLast: method is not support"
End Sub

' 删除最后一个元素，并返回删除的数据
'
' @return 被删除的最后一个元素
'         如果当前数组引用中已经没有元素了，将会返回一个 `Empty`
Public Function IObjArrayRef_pollLast() As Variant
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_pollLast: method is not support"
End Function

' 获取数组引用中的最后一个元素
' 如果当前数组引用是空的，则返回 `Empty`
Public Function IObjArrayRef_getLast() As Variant
    If sliceMaxIndex = -1 Then
        Set IObjArrayRef_getLast = Nothing
    Else
        IObjArrayRef_getLast = delegatedArrRef.unsafeItem(sliceStartIndex + sliceMaxIndex * sliceStep)
    End If
End Function

' 合并另一个基础类型数组引用中的数据
' @param others, 另一个基础类型数组引用
Public Sub IObjArrayRef_mergeArrRef(ByRef others As IObjArrayRef)
    Err.Raise 10000, Description:="ObjArrayRefSlice.IObjArrayRef_mergeArrRef: method is not support"
End Sub

' 生成当前数组引用的切片
' @optional-param startIdx 起始位置
' @optional-param stopIdx 终止位置(最终不会包含 stop 的索引)
' @optional-param step 步长
'                      默认值 = 1
'                      不能是 0，如果设置了 0 将会抛出异常
Public Function IObjArrayRef_slice(Optional ByRef startIdx As Variant, Optional ByRef stopIdx As Variant, Optional ByRef step As Variant = 1) As IObjArrayRef
    ' 生成切片
    Dim result As ObjArrayRefSlice
    Set result = New ObjArrayRefSlice
    result.Init Me, startIdx, stopIdx, Step

    ' return
    Set IObjArrayRef_slice = result
End Function

' 克隆当前数组引用对象，并生成一个 `IObjArrayRef` 类型的真实数组引用
Public Function IObjArrayRef_clone() As IObjArrayRef
    ' 1. 创建一个和切片元素数量相同长度的数组
    Dim result As ObjArrayRef
    Set result = UtilsArrayRef.createRealObjArrayRef(sliceMaxIndex + 1)

    ' 2. 遍历，从原始数组引用中获取数据，并保存到 result
    Dim i As Long
    For i = 0 To sliceMaxIndex
        result.Add delegatedArrRef.unsafeItem(sliceStartIndex + i * sliceStep)
    Next

    ' return
    Set IObjArrayRef_clone = result
End Function

' 复制索引 `[startIdx, endIdx)` 范围的内数据到新的数组引用对象中
' @optional-param startIdx 起始索引
' @optional-param endIdx 结束索引（不包括）
Public Function IObjArrayRef_copyToNewArray(Optional ByRef startIdx As Long = 0, Optional ByRef endIdx As Long = -1) As IObjArrayRef
    Dim result As ObjArrayRef

    ' 1. 如果没有元素，则返回一个空对象
    If sliceMaxIndex = -1 Or startIdx > sliceMaxIndex  Then
        Set result = New ObjArrayRef
        result.Init 0
        Set IObjArrayRef_copyToNewArray = result
        Exit Function
    End If

    ' 2. 计算真实的索引
    Dim realStartIdx As Long
    ' 这里表示最后一个元素的索引位置
    Dim realLastIdx As Long
    If startIdx < 0 Then
        realStartIdx = 0
    Else
        realStartIdx = startIdx
    End If

    If endIdx < 0 Then
        realLastIdx = sliceMaxIndex
    ElseIf endIdx > sliceMaxIndex Then
        realLastIdx = sliceMaxIndex
    Else 
        realLastIdx = endIdx - 1
    End If

    ' 快速失败
    ' 如果起始索引 > 最后一个元素的索引，则返回一个空结果
    If realStartIdx > realLastIdx Then
        Set result = New ObjArrayRef
        result.Init 0
        Set IObjArrayRef_copyToNewArray = result
        Exit Function
    End If

    ' 3. 拷贝数据
    Set result = New ObjArrayRef
    result.Init (realLastIdx - realStartIdx + 1)
    Set IObjArrayRef_copyToNewArray = result

    Dim i As Long
    For i = realStartIdx To realLastIdx
        IObjArrayRef_copyToNewArray.Add delegatedArrRef.unsafeItem(sliceStartIndex + i * sliceStep)
    Next

End Function

' 获取当前数组引用中，数组缓存的真实长度
Public Function IObjArrayRef_unsafeRealSize() As Long
    IObjArrayRef_unsafeRealSize = sliceMaxIndex + 1
End Function
